/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package net.worlds;

import java.awt.Color;
import java.awt.DisplayMode;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.TexturePaint;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.RescaleOp;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.worlds.object.Camera;
import net.worlds.object.Environment;
import net.worlds.object.GameObject;
import net.worlds.object.Part;
import net.worlds.object.Prop;
import net.worlds.object.Water;
import net.worlds.ui.PartsTree;
import net.worlds.ui.PropsTable;
import net.worlds.ui.UI;
import net.worlds.ui.edge.EDGESettings;
import net.worlds.ui.edge.EMain;
import org.jbox2d.collision.AABB;
import org.jbox2d.collision.shapes.Shape;
import org.jbox2d.common.Vec2D;


public abstract class MainView extends View{
    private Vec2D static_camPosition = null;
    private Vec2D camPosition = null;
    public float scaleFactor = 8;
    public Part followObject = null;
    public Vec2D offset = null;
    public float rotateOffset;
    public Vec2D followObjectPos = new Vec2D(0, 0);
    public boolean isFullscreen = false;
    private DisplayMode dispMode = Util.getGraphicsDevice().getDisplayMode();
    private PropsTable propsTable = new PropsTable();
    private PartsTree partsTree = new PartsTree();
    public boolean showUI = true;
    protected Shape[] queryResult;
    protected static HashMap<Integer, ArrayList<GameObject>> resultLayers = new HashMap<Integer, ArrayList<GameObject>>(3);
    protected Paint backgroundPaint = null;
    protected BufferedImage resizedBackground = null;
    protected float time = 0;
    protected float dayAlpha;
    protected float nightAlpha;
    protected boolean antialiasing = false;
    protected final Color defaultToolColor = new Color(100, 100, 100, 200);
    protected final Color selectedToolColor = new Color(170, 170, 185, 255);
    protected final Color transparentBlack = new Color(0, 0, 0, 175);
    protected final Color healthColor = new Color(10, 170, 40);
    protected final Color topBarColor = new Color(127, 136, 154);
    protected final Color selectionRectBorderColor = new Color(64, 64, 64, 192);
    protected final Color selectionRectBackgroundColor = new Color(192, 192, 192, 64);
    protected BufferedImage heartImage;
    protected BufferedImage loadingBackground;
    protected BufferedImage cursorImage;
    protected BufferedImage helpOn;
    protected BufferedImage helpOff;
    protected BufferedImage night;
    
    protected static final Color c1 = new Color(113,141,191);
    protected static final Color c2 = new Color(78, 92, 119);
    
    protected EMain topUI;
    protected EMain overlayUI;
    
    public Vec2D centerToCorner = new Vec2D(-width/2, -height/2);

    public MainView()
    {
        static_camPosition = new Vec2D(0, 0);
        camPosition = new Vec2D(0, 0);
        offset = new Vec2D(0, 0);
        for(int i = 0; i < 3; i++)
        {
            resultLayers.put(i, new ArrayList<GameObject>(WorldsSettings.MAX_OBJECTS));
        }
        try {
            heartImage = AssetManager.loadImage("heart", "resource://heart.png");
            cursorImage = AssetManager.loadImage("cursor", "resource://playercursor.png");
            helpOn = AssetManager.loadImage("helpon", "resource://help_on.png");
            helpOff = AssetManager.loadImage("helpoff", "resource://help_off.png");
            night = AssetManager.loadImage("night", "resource://night.png");
        }    catch (IOException ex) {
            heartImage = null;
            ex.printStackTrace();
        }
    }
    public void setAntialiasing(boolean v)
    {
        antialiasing = v;
    }
    public PropsTable getPropsTable()
    {
        return propsTable;
    }
    public PartsTree getPartsTree()
    {
        return partsTree;
    }
    public void setScaleFactor(float v) throws WorldsException
    {
        scaleFactor = v;
        if(Main.getGame() != null && Main.getGame().currentCamera != null)
            Main.getGame().currentCamera.setProp("ScaleFactor", v, false);
    }
    public void setCamPosition(Vec2D v, boolean fireEvent) throws WorldsException
    {
        camPosition = v;
        if(fireEvent && Main.getGame() != null && Main.getGame().currentCamera != null)
            Main.getGame().currentCamera.setProp("Position", v, false);
    }
    public void update()
    {
        if(followObject != null)
        {
            followObjectPos.set(followObject.getPosition());
            Vec2D d = followObjectPos.sub((camPosition));
            camPosition = camPosition.add(d.div(4));
            //camPosition = new Vec2D(followObjectPos.x, followObjectPos.y + 10);
        }
       static_camPosition = camPosition.add(offset);
    }
    public Vec2D getCamPosition()
    {
        return static_camPosition;
    }
    public void translateCam(Vec2D tvec) throws WorldsException
    {
        camPosition = camPosition.add(tvec);
        if(Main.getGame() != null && Main.getGame().currentCamera != null)
            Main.getGame().currentCamera.setProp("Position", camPosition, false);
    }
    public void setViewPropertiesFromCamera(Camera camera)
    {
        try {
            scaleFactor = (float) camera.getFloatProp("ScaleFactor");
            GameObject obj = camera.getObjectProp("FollowObject");
            if(obj instanceof Part)
            {
                followObject = (Part) obj;
                camPosition = followObject.getPosition();
            }
            else
               camPosition = camera.getVec2DProp("Position");
            offset = camera.getVec2DProp("Offset");
            rotateOffset = camera.getFloatProp("RotationOffset");
        } catch (WorldsException ex) {
            Logger.getLogger(MainView.class.getName()).log(Level.SEVERE, null, ex);
        }

    }
    /*public void enterFullscreen()
    {
        if(isFullscreen)
            return;
        if(UI.toolsVisible)
            mainFrame.getGamePanel().getEditorTop().resizeWindowItem.setEnabled(false);
        mainFrame.dispose();
        mainFrame.setExtendedState(mainFrame.getExtendedState()|JFrame.MAXIMIZED_BOTH);
        mainFrame.setUndecorated(true);
        mainFrame.setVisible(true);
        isFullscreen = true;
    }
    public void exitFullscreen()
    {
        if(!isFullscreen)
            return;
        if(UI.toolsVisible)
        mainFrame.getGamePanel().getEditorTop().resizeWindowItem.setEnabled(true);
        mainFrame.dispose();
        mainFrame.setUndecorated(false);
        mainFrame.setVisible(true);
        mainFrame.setSize(1024, 768);
        mainFrame.setLocationRelativeTo(null);
        isFullscreen = false;
    }*/
    protected void drawObjects(Graphics2D g, World world, float worldWidth, float worldHeight)
    {
        List<GameObject> objects = world.getObjects();
        for(int i = 0; i < 3; i++)
        {
            resultLayers.get(i).clear();
        }

        AABB aabb = new AABB();
        aabb.lowerBound = new Vec2D((float) (Main.getView().getCamPosition().x - worldWidth / 2f), (float) (Main.getView().getCamPosition().y - worldHeight / 2f));
        aabb.upperBound = new Vec2D((float) (Main.getView().getCamPosition().x + worldWidth / 2f), (float) (Main.getView().getCamPosition().y + worldHeight / 2f));
        try
        {
            if(!world.isStepping)
                queryResult = world.query(aabb, 32768);
            if(queryResult != null)
            {
                for(Shape s : queryResult)
                {
                    Part p = s.getUserData();
                    if(p != null)
                        try {
                        resultLayers.get(p.getIntProp("Layer")).add(p);
                    } catch (WorldsException ex) {
                    }
                }
            }
        }
        catch(ArrayIndexOutOfBoundsException ex)
        {
            return;
        }

        for(int i = 0; i < 3; i++)
        {
            List<GameObject> objectLayer = world.getLayer(i);
            synchronized(objectLayer)
            {
                for(GameObject obj : objectLayer)
                {
                    if(resultLayers.get(i).contains(obj) || !(obj instanceof Part))
                    {
                        try {
                            obj.draw(g);
                        } catch (Exception ex) {
                            System.err.println("Error drawing object "+obj);
                            ex.printStackTrace();
                        }
                    }
                }
            }
            if(i == 1)
            {
                List<GameObject> waters = world.getObjectsByType(Water.class);
                for(GameObject water : waters)
                {
                    try
                    {
                        ((Water)water).draw(g, 80);
                    } catch (Exception ex) {
                        System.err.println("Error drawing object "+water);
                        ex.printStackTrace();
                    }
                }
            }
        }
        synchronized(objects)
        {
            for(GameObject obj : objects)
            {
                try {
                    if(!(obj instanceof Prop))
                        obj.draw(g);
                } catch (Exception ex) {
                    System.err.println("Error drawing object "+obj);
                    ex.printStackTrace();
                }
            }
        }
    }
    public void draw(Graphics2D g, int viewWidth, int viewHeight)
    {
        World world = Main.getGame().getCurrentWorld();
        float worldWidth = viewWidth / Main.getView().scaleFactor;
        float worldHeight = viewWidth / Main.getView().scaleFactor;
        drawWorld(g, world, viewWidth, viewHeight, worldWidth, worldHeight);
        drawGameUI(g, world, viewWidth, viewHeight, worldWidth, worldHeight);
    }
    @Override
    public void drawUI(Graphics2D g, int width, int height, int viewWidth, int viewHeight)
    {
        if(overlayUI != null)
        {
            overlayUI.setSize(viewWidth, viewHeight);
            overlayUI.render(g);
        }
        if(topUI != null)
        {
            topUI.setSize(width, height);
            topUI.render(g);
        }
        
    }
    protected abstract void drawGameUI(Graphics2D g, World world, int viewWidth, int viewHeight, float worldWidth, float worldHeight);
    protected void drawWorld(Graphics2D g, World world, int viewWidth, int viewHeight, float worldWidth, float worldHeight)
    {
        g.setFont(EDGESettings.defaultFont);
        
        Environment env = world.environment;
        if(env != null)
            time = env.fracTime;
        else
            time = 0.5f;
        Main.getView().update();
        if(!UI.viewVisible)
            return;
        Graphics2D g2 = (Graphics2D) g;
        if(antialiasing)
        {
            ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
        }

        Vec2D vec = new Vec2D(-viewWidth/2, viewHeight/2);
        Vec2D worldStart = Main.getView().getCamPosition().add(vec.toWorldUnits());
        if(worldStart.x < 0)
            worldStart.x = 0;
        if(worldStart.y > world.getHeight())
            worldStart.y = world.getHeight();
        Vec2D worldStop = Main.getView().getCamPosition().add(new Vec2D(viewWidth/2, -viewHeight/2).toWorldUnits());
        if(worldStop.x > world.getWidth())
            worldStop.x = (world.getWidth());
        if(worldStop.y < 0)
            worldStop.y = 0;
        Vec2D screenStart = worldStart.worldToScreen(this);
        Vec2D screenStop = worldStop.worldToScreen(this);
        g.setColor(Color.black);
        g.fillRect(0, 0, viewWidth, viewHeight);
        g.setColor(world.getSkyColor());
        if(world.getBackgroundImage() == null)
            g.fillRect((int) screenStart.x, (int) screenStart.y, (int) screenStop.x - (int) screenStart.x, (int) screenStop.y - (int) screenStart.y);
        else
        {
            dayAlpha = 1;
            nightAlpha = 1;
            if(time > 0.27 && time < 0.29)
            {
                nightAlpha = (float) ((0.29f - time) / 0.02f);
                dayAlpha = 1 - nightAlpha;
            }
            else if(time > 0.77 && time < 0.79)
            {
                dayAlpha = (float) ((0.79f - time) / 0.02f);
                nightAlpha = 1 - dayAlpha;
            }
            RescaleOp dayOp = null;
            RescaleOp nightOp = null;
            if(dayAlpha < 1 || nightAlpha < 1)
            {
                float[] dayScales = { 1f, 1f, 1f, dayAlpha};
                float[] nightScales = { 1f, 1f, 1f, nightAlpha};
                float[] offsets = new float[4];
                dayOp = new RescaleOp(dayScales, offsets, null);
                nightOp = new RescaleOp(nightScales, offsets, null);
            }
            if(time > 0.27 && time < 0.79)
            {
                if(world.scaleBackground)
                    ((Graphics2D)g).drawImage(resizedBackground, dayOp, 0, 0);
                else
                {
                    Paint oldPaint = ((Graphics2D)g).getPaint();
                    if(world.scrollBackground)
                    {
                        Vec2D originScreen = Vec2D.ORIGIN.worldToScreen(this);
                        int w = (int) (world.getBackgroundImage().getWidth() * (Main.getView().scaleFactor/8));
                        int h = (int) (world.getBackgroundImage().getHeight() * (Main.getView().scaleFactor / 8));
                        ((Graphics2D)g).setPaint(new TexturePaint(world.getBackgroundImage(), new Rectangle((int) originScreen.x, (int) originScreen.y - h, w, h)));
                    }
                    else
                        ((Graphics2D)g).setPaint(backgroundPaint);
                    ((Graphics2D)g).fillRect(0, 0, width, height);
                    ((Graphics2D)g).setPaint(oldPaint);
                }

            }
            if(time < 0.29 || time > 0.77)
            {
                ((Graphics2D)g).drawImage(night, nightOp, 0, 0);
                g.setColor(new Color(0, 0, 0, 128));
                g.fillRect(0, 0, width, height);
            }
        }

        AffineTransform originalTransform = g2.getTransform();
        AffineTransform rotated = AffineTransform.getRotateInstance(Main.getView().rotateOffset);
        g2.setTransform(rotated);
        try
        {
            drawObjects(g, world, worldWidth, worldHeight);
        }
        catch(Exception ex)
        {
            if(Main.debug)
                ex.printStackTrace();
        }
        g2.setTransform(originalTransform);
        if(env != null && env.fogAmount > 0)
        {
            g.setColor(new Color(env.fogColor.getRed(), env.fogColor.getGreen(), env.fogColor.getBlue(), env.fogAmount));
            g.fillRect(0, 0, width, height);
        }
        if(time < 0.29 || time > 0.77)
        {
            g.setColor(new Color(0, 0, 0, (int) (nightAlpha * 0.7 * 160)));
            g.fillRect(0, 0, width, height);
        }
    }
    protected void drawText(Graphics g, String text, int textX, int textY)
    {
        g.setFont(defaultFont);
        g.setColor(Color.black);
        g.drawString(text, textX + 1, textY + 1);
        g.setColor(Color.white);
        g.drawString(text, textX, textY);
    }
    public void updateBackground()
    {
        World world = Main.getGame().getCurrentWorld();
        if(world.getBackgroundImage() != null)
        {
            resizedBackground = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            Graphics2D g2 = resizedBackground.createGraphics();
            if(world.scaleBackground)
            {
                g2.drawImage(world.getBackgroundImage(), 0, 0, width, height, null);
            }
        }
    }
    public void updatePaint(World world)
    {
        if(world.getBackgroundImage() != null)
        {
            Rectangle rect = new Rectangle(0, 0, world.getBackgroundImage().getWidth(), world.getBackgroundImage().getHeight());
            backgroundPaint = new TexturePaint(world.getBackgroundImage(), rect);
        }
    }
    @Override
    public void setSize(int x, int y)
    {
        super.setSize(x, y);
        centerToCorner = new Vec2D(-width/2, -height/2);
    }
    public void onCreated(){}
    public abstract EMain getOverlay();
    public abstract EMain getTop();
}
